home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / TreeView / TreeController.m < prev    next >
Text File  |  1995-06-12  |  7KB  |  199 lines

  1.  
  2. #import "TreeController.h"
  3. #import "NamedTree.h"
  4. #import "TreeView.h"
  5.  
  6. @implementation TreeController
  7.  
  8. - init
  9. {
  10.     [super init];
  11.     first = YES;
  12.     nextX = 200;
  13.     nextY = 600;
  14.     return self;
  15. }
  16.  
  17. - info:sender // bring up the info panel, obviously
  18. {
  19.     if(!infoPanel)
  20.         [NXApp loadNibSection:"InfoPanel.nib" owner:self withNames:NO];
  21.     return [infoPanel orderFront:sender];
  22. }
  23.  
  24. - open:sender
  25. {    // use open panel to select a file -- only with .tree extension.
  26.     // only one file may be loaded at a time.
  27.     const char *const *files;
  28.     char *file;
  29.     const char *dir;
  30.     static const char *const ft[2] = {"tree", NULL};
  31.  
  32.     id openPanel = [OpenPanel new];
  33.     [openPanel allowMultipleFiles:NO];
  34.     if (first) {
  35.         [openPanel runModalForDirectory:[[NXBundle mainBundle] directory]
  36.                 file:NULL types:ft];
  37.         first = NO;
  38.     } else  [openPanel runModalForTypes:ft];
  39.     files = [openPanel filenames];
  40.     dir = [openPanel directory];
  41.     file = malloc(strlen(files[0]) + strlen(dir) + 8);
  42.     strcpy(file, dir);
  43.     strcat(file,"/");
  44.     strcat(file, files[0]);
  45.     strcat(file, "\0");
  46.     [self openFile:file];
  47.     return self;
  48. }
  49.  
  50. char nextChar; // This allows me to do single character lookahead in parse
  51.  
  52. id readToNewline(FILE *file) // used to parse a file; reads a line at a time
  53. {    // returns a string object... reads until EOL to get string value.
  54.     id newString = [[String alloc] init];
  55.     char *buffer = (char *)malloc(1024);    // should be plenty big
  56.     char *spot = buffer;
  57.     while (nextChar != '\n') {
  58.         spot[0] = nextChar; spot++; nextChar = fgetc(file);
  59.     }
  60.     spot[0] = '\0'; // terminate the string
  61.     nextChar = fgetc(file);
  62.     [newString setString:buffer];
  63.     free(buffer);
  64.     return newString;
  65. }
  66.  
  67. // This actually opens a file.  WorkSpace and Open panel methods both
  68. // eventually get to here to do the real work.  This code is pretty much
  69. // worth ignoring, unless you _really_ care about how I read in the
  70. // files.  It's mostly specific to the file format so it's not
  71. // generally useful.  The framework for this code came from the
  72. // code in my "Viewer.app" that is in with some PD raytracers I ported
  73. // to the NeXT--allows viewing an rgb bitmap file; I wrote it before
  74. // GW and ImageViewer existed...  (See raytracers.tar.Z on sonata/orst)
  75. - (BOOL)openFile:(const char *)name
  76. {
  77.     id alert, aString, treeName, rootNode, workingNode, tempNode;
  78.     id newString, stack = [[List alloc] init];
  79.     int indLevel, numSpaces, indent = 0;
  80.     char *tempString;
  81.     BOOL rStat = YES;
  82.     FILE *file;
  83.     // for debugging:
  84.     //NXStream *out = NXOpenFile(fileno(stdout), NX_WRITEONLY);
  85.  
  86.     // get a new doc window.
  87.     [NXApp loadNibSection:"DocWindow.nib" owner:self withNames:NO];
  88.     [[treeView window] setTitleAsFilename:name];
  89.     // put up an alert panel to let user know we're busy
  90.     alert = NXGetAlertPanel(NULL, "Reading tree and creating image.",
  91.             NULL, NULL, NULL);
  92.     [alert makeKeyAndOrderFront:self];
  93.     // Read the tree file.  Note that I don't do error checking.  Badness.
  94.     file = fopen(name, "r");
  95.     nextChar = fgetc(file);    // prime the system
  96.     // first line is tree's name.
  97.     treeName = readToNewline(file);
  98.     aString = readToNewline(file);    // get the name of the root node.
  99.     rootNode = [[[NamedTree alloc]
  100.             initLabelString:aString] setTreeName:treeName];
  101.     [stack insertObject:rootNode at:0];
  102.     workingNode = rootNode;
  103.     // figure out the indentation
  104.     while (nextChar == ' ') {
  105.         indent++;
  106.         nextChar = fgetc(file);
  107.     }
  108.     aString = readToNewline(file); // get name of child node
  109.     tempNode = [[[NamedTree alloc]
  110.             initLabelString:aString] setTreeName:treeName];
  111.     [workingNode addBranch:tempNode];
  112.     [stack insertObject:tempNode at:0];
  113.     workingNode = tempNode;
  114.     // now that we know the file's char's, we read in the other nodes
  115.     // I use a List object as if it were a stack and push parent nodes on
  116.     // it while working on children rather than doing a recursive function.
  117.     // the comments are sparse, just know that it's mostly pushing and
  118.     // popping from the stack to get at the right parent to add a child to.
  119.     while (!feof(file)) {
  120.         aString = readToNewline(file); // next node name + indentation.
  121.         // find out # of indentation spaces and strip them off
  122.         // *** this like gives a warning: ignore it, it's unimportant here.
  123.         tempString = [aString stringValue]; numSpaces = 0;
  124.         while (tempString[0] == ' ') {
  125.             numSpaces++; tempString++;
  126.         }
  127.         indLevel = numSpaces / indent;
  128.         if (indLevel == ([stack count] - 1)) { // same level as last object
  129.             [stack removeObjectAt:0];
  130.             workingNode = [stack objectAt:0];
  131.             newString = [[String alloc] initString:tempString];
  132.             [aString free];
  133.             tempNode = [[[NamedTree alloc]
  134.                     initLabelString:newString] setTreeName:treeName];
  135.             [workingNode addBranch:tempNode];
  136.             [stack insertObject:tempNode at:0];
  137.             workingNode = tempNode;
  138.         } else if (indLevel == ([stack count])) { // child of last node
  139.             newString = [[String alloc] initString:tempString];
  140.             [aString free];
  141.             tempNode = [[[NamedTree alloc]
  142.                     initLabelString:newString] setTreeName:treeName];
  143.             [workingNode addBranch:tempNode];
  144.             [stack insertObject:tempNode at:0];
  145.             workingNode = tempNode;
  146.         } else if (indLevel < [stack count]) { // higher level, so pop
  147.             while (indLevel < [stack count]) { // pop until at right level
  148.                 [stack removeObjectAt:0];
  149.                 workingNode = [stack objectAt:0];
  150.             } // now add new node since we're at the level
  151.             newString = [[String alloc] initString:tempString];
  152.             [aString free];
  153.             tempNode = [[[NamedTree alloc]
  154.                     initLabelString:newString] setTreeName:treeName];
  155.             [workingNode addBranch:tempNode];
  156.             [stack insertObject:tempNode at:0];
  157.             workingNode = tempNode;
  158.         } else { // typically, if user goes in two levels at once, which
  159.             // doesn't make any sense semantically
  160.             fprintf(stderr, "Error: level too deep!\n");
  161.             rStat = NO;
  162.         }
  163.     }
  164.     // Debugging code to pretty print the parsed tree.  If this output
  165.     // is correct, then we know that the parse was OK.
  166.     //printf("\nHere's the parsed tree:\n");
  167.     //printf("Tree name:  \"%s\".", [treeName stringValue]);
  168.     //[rootNode dumpTree:out level:0 indent:"   "];
  169.     //printf("\n\n");
  170.     //NXClose(out);
  171.     // end debug code
  172.     // rStat = return status of tree reader  YES = success
  173.     // Now attach the Tree to the TreeView...
  174.     [treeView attachTree:rootNode];
  175.     // and now bring up the window for the user and get rid of the alert
  176.     [[treeView window] moveTo:nextX :(nextY-218)];
  177.     [alert orderOut:self];
  178.     [alert free];
  179.     nextX += 22; nextY -= 25;
  180.     if (nextX > 370) {
  181.         nextX = 200; nextY = 600;
  182.     }
  183.     [[treeView window] makeKeyAndOrderFront:self];
  184.     return rStat;
  185. }
  186.  
  187.  
  188. // The next two methods allow the WorkSpace to open a .tree file when
  189. // it is double-clicked.  (Or any file that's cmd-dragged over our icon.)
  190.  
  191. - (BOOL)appAcceptsAnotherFile:sender { return YES; }
  192. - (int)app:sender openFile:(const char *)file type:(const char *)type
  193. {
  194.     return [self openFile:file];
  195. }
  196.  
  197.  
  198. @end
  199.